home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 37 / Amiga Format CD37 (1999-02-16)(Future Publishing)(GB)(Track 1 of 3)[!][issue 1999-03].iso / -screenplay- / shareware / invasionforce / source / main_menu.c < prev    next >
C/C++ Source or Header  |  1999-01-09  |  37KB  |  1,241 lines

  1. /*
  2.    main_menu.c -- main program menu module
  3.  
  4.    This module is intended to handle the opening and title screen, and
  5.    provide a `home base' for accessing the other user modules: the map
  6.    editor, the options editor and the game play.  It also provides some
  7.    all-purpose utility functions.
  8.  
  9.    This source code is free.  You may make as many copies as you like.
  10.  
  11.  ***********************************************************************
  12.  ***            THE GAME OF INVASION FORCE FOR THE AMIGA             ***
  13.  ***                         by Tony Belding                         ***
  14.  ***********************************************************************
  15.  
  16.  The game was written with the SAS/C compiler.
  17.  Compatibility with other compilers is anybody's guess.
  18. */
  19.  
  20. // standard header for all program modules
  21. #include "global.h"
  22.  
  23. #define CE_RESTART (-17)
  24.  
  25. //    GLOBAL VARIABLES
  26.  
  27. char *version = "$VER: Invasion Force 0.17 (22.5.97)";
  28. short revision = 17;
  29.  
  30.  
  31. // an all-purpose workspace
  32. char foo[256], bar[256];
  33.  
  34. // system (Intuition, etc.) globals
  35. BPTR my_console = NULL;
  36. struct Screen *title_screen = NULL;
  37. struct BitMap title_bmp = { 0,0,0,0,0, 0,0,0,0, 0,0,0,0};
  38. __far extern char title_picture[];
  39. struct Screen *map_screen = NULL;
  40. APTR vi = NULL;      // visual-info handle for GadTools
  41. struct Window *map_window = NULL;
  42. struct Window *terrain_window = NULL;
  43. struct Menu *main_menu_strip = NULL;
  44. struct ReqToolsBase *ReqToolsBase = NULL;
  45. struct MEDPlayerBase *MEDPlayerBase = NULL;
  46.  
  47. char default_sound[MAX_SOUNDS][216] = {                // default sound effects
  48.    "PROGDIR:Data/Sound/8SVX.donk",
  49.    "PROGDIR:Data/Sound/8SVX.boom",
  50.    "PROGDIR:Data/Sound/8SVX.death_cry",
  51.    "PROGDIR:Data/Sound/8SVX.low_smash",
  52.    "PROGDIR:Data/Sound/8SVX.yeah"
  53. };
  54.  
  55. char current_sound[MAX_SOUNDS][216];     // currently active sound effects
  56.  
  57. char win_title[80];     // area to store window title text
  58.  
  59. int disp_wd, disp_ht;   // display size in MAP HEXES, not pixels
  60.  
  61. struct Gadget *context = NULL,
  62.    *vert_scroller = NULL,
  63.    *horz_scroller = NULL;
  64.  
  65. APTR vs_si=NULL, hs_si=NULL;
  66.  
  67. // the identifiers for my various fonts
  68. struct TextAttr topaz8 = { (STRPTR)"topaz.font",8,0,0 };
  69. struct TextAttr topaz9 = { (STRPTR)"topaz.font",9,0,0 };
  70. struct TextAttr topaz11 = { (STRPTR)"topaz.font",11,0,0 };
  71. struct TextAttr topaz11bold = { (STRPTR)"topaz.font",11,FSF_BOLD,0 };
  72.  
  73. // the default requester TRUE or FALSE exit keys
  74. // some versions of Intution allowed the user to alter these using a PREFS tool,
  75. // but I don't have any documentation on how to handle that, or even if it's still
  76. // supposed to be supported -- I suspect it is not
  77. char req_true = 'V', req_false = 'B';
  78.  
  79. struct Emp2Prefs {
  80.    ULONG DisplayID;
  81.    UWORD DisplayWidth;
  82.    UWORD DisplayHeight;
  83.    UWORD OverscanType;
  84. } prefs;
  85.  
  86. struct newEmp2Prefs {
  87.    ULONG DisplayID;        // display specs for main map screen
  88.    UWORD DisplayWidth;
  89.    UWORD DisplayHeight;
  90.    UWORD OverscanType;
  91.    ULONG WM_DisplayID;     // specs for World Map display
  92.    UWORD WM_DisplayWidth;
  93.    UWORD WM_DisplayHeight;
  94.    UWORD WM_OverscanType;
  95.    UWORD WM_Fatness;       // pixel magnification
  96. } newprefs;
  97.  
  98. //    BEGIN OF PROGRAM FUNCTIONS
  99.  
  100. void print(string)
  101. char *string;
  102. {  // print to the console: used mainly for error reports & debugging.
  103.    if (my_console)
  104.       Write(my_console,string,strlen(string));
  105.    else
  106.       Write(Output(),string,strlen(string));
  107. }
  108.  
  109.  
  110. // alarm() gives the user a message -- using a ReqTools requester if possible
  111. // (i.e. if ReqTools.library is available) or otherwise using print() to console
  112.  
  113. void alarm(string)
  114. char *string;
  115. {  // tell the user about a bug or other error
  116.    DisplayBeep(NULL);
  117.    if (ReqToolsBase)
  118.       (void)rtEZRequestTags(string,"DRAT!", NULL, NULL,
  119.          RT_Window, map_window,
  120.          RT_ScreenToFront, TRUE,
  121.          RT_LockWindow, FALSE,
  122.          TAG_DONE);
  123.    else {
  124.       print(string);
  125.       print("\n");
  126.       Delay(300L);   // so I can read it before the window closes
  127.    FI
  128. }
  129.  
  130.  
  131. /*
  132.    UPDATE: I have expanded the function of clean_exit() to restart the program
  133.    as well as terminate it.  When clean_exit() is passed the error value
  134.    CE_RESTART, it will close down everything but will not terminate the program
  135.    execution.
  136. */
  137.  
  138. void clean_exit(error,text)
  139. int error;
  140. char *text;
  141. {  // de-allocate and close EVERTHING, then exit the program
  142.    /*
  143.       I may try to find a more efficient way to pass messages later on, to
  144.       avoid repeating error text throughout my program.
  145.    */
  146.    if (text)
  147.       alarm(text);
  148.  
  149.    // De-allocate the sound effects and music player.
  150.    freeSounds();
  151.    FreePlayer();
  152.  
  153.    // free data structures
  154.    cleanup_game();
  155.  
  156.    // close the map window
  157.    if (map_window) {
  158.       ClearMenuStrip(map_window);
  159.       CloseWindow(map_window);
  160.       map_window = NULL;
  161.    FI
  162.  
  163.    // free the clipping region
  164.    if (map_region) {
  165.       DisposeRegion(map_region);
  166.       map_region = NULL;
  167.    FI
  168.    if (bar_region) {
  169.       DisposeRegion(bar_region);
  170.       bar_region = NULL;
  171.    FI
  172.  
  173.    // free my scroller gadgets
  174.    if (hs_si) {
  175.       FreeVec(hs_si);
  176.       hs_si = NULL;
  177.    FI
  178.    if (vs_si) {
  179.       FreeVec(vs_si);
  180.       vs_si = NULL;
  181.    FI
  182.    FreeGadgets(context);
  183.    context = NULL;
  184.  
  185.    // free the menu strips
  186.    if (main_menu_strip) {
  187.       FreeMenus(main_menu_strip);
  188.       main_menu_strip = NULL;
  189.    FI
  190.    if (editor_menu_strip) {
  191.       FreeMenus(editor_menu_strip);
  192.       editor_menu_strip = NULL;
  193.    FI
  194.    if (move_menu_strip) {
  195.       FreeMenus(move_menu_strip);
  196.       move_menu_strip = NULL;
  197.    FI
  198.    if (vey_menu_strip) {
  199.       FreeMenus(vey_menu_strip);
  200.       vey_menu_strip = NULL;
  201.    FI
  202.    if (prod_menu_strip) {
  203.       FreeMenus(prod_menu_strip);
  204.       prod_menu_strip = NULL;
  205.    FI
  206.  
  207.    // close the ViewInfo handle
  208.    if (vi) {
  209.       FreeVisualInfo(vi);
  210.       vi = NULL;
  211.    FI
  212.  
  213.    // Close the screens.
  214.    if (title_screen) {
  215.       CloseScreen(title_screen);
  216.       title_screen = NULL;
  217.    FI
  218.    if (map_screen) {
  219.       CloseScreen(map_screen);
  220.       map_screen = NULL;
  221.    FI
  222.  
  223.    // Close the console.
  224.    if (my_console) {
  225.       Close(my_console);
  226.       my_console = NULL;
  227.    FI
  228.  
  229.    // Close the libraries.
  230.    CloseLibrary((APTR)ReqToolsBase);
  231.    CloseLibrary((APTR)MEDPlayerBase);
  232.  
  233.    if (error!=CE_RESTART)
  234.       exit(error);
  235.  
  236. }
  237.  
  238.  
  239. void open_libraries()
  240. {  // open libs, only those not opened by SAS/C itself
  241.    ReqToolsBase=(struct ReqToolsBase *)OpenLibrary(REQTOOLSNAME,REQTOOLSVERSION);
  242.    if (ReqToolsBase==NULL)
  243.       clean_exit(1,"ERROR: Unable to open ReqTools.library!");
  244.  
  245.    MEDPlayerBase=(struct MEDPlayerBase *)OpenLibrary("medplayer.library",0);
  246.    if (MEDPlayerBase==NULL)
  247.       clean_exit(1,"ERROR: Unable to open MedPlayer.library!");
  248.  
  249. }
  250.  
  251.  
  252. // alert() uses ReqTools to pop up a quick-and-easy alert box, though
  253. // it will be better sometimes to go straight for rtEZRequestTags()
  254.  
  255. int alert(window,title,hail,gads)
  256. struct Window *window;
  257. char *title, *hail, *gads;
  258. {  // quick & dirty alert boxes via the ReqTools library
  259.    // these are best used for reporting non-fatal errors
  260.    return (int)rtEZRequestTags(hail,gads,NULL,NULL,
  261.       RTEZ_ReqTitle,title,
  262.       RT_Window,window,
  263.       RT_ReqPos,REQPOS_CENTERWIN,
  264.       RT_LockWindow, TRUE,
  265.       TAG_END );
  266. }
  267.  
  268.  
  269. /*
  270.    The Post-It=AE note system is used to inform the user about ongoing,
  271.    time-consuming processes, such as loading from disk (I'm thinking floppy
  272.    disk here) or the artificial opponents moving.  It puts a small window with
  273.    the message on screen in the upper left corner, then removes it when the
  274.    process is complete.  This method is more informative than merely showing
  275.    a busy pointer.
  276.  
  277.    NOTE: Certain versions of reqtools.library have a bug causing the note to
  278.    extend across the full width of the screen.  This is supposed to be fixed
  279.    RSN.  Until then, it's merely a nuisance.
  280.  
  281.    The application must keep track of the ReqTools handle to close the note.
  282. */
  283.  
  284. struct rtHandlerInfo *post_it(message)
  285. char *message;
  286. {  // stick a Post-It=AE note up on the display
  287.    struct rtHandlerInfo *handle;
  288.  
  289.    (void)rtEZRequestTags(message,NULL,NULL,NULL,
  290.       RT_Window, map_window,
  291.       RT_ReqPos, REQPOS_TOPLEFTWIN,
  292.       RT_LeftOffset,5L,
  293.       RT_TopOffset,15L,
  294.       RT_LockWindow, FALSE,
  295.       RT_ReqHandler, &handle,
  296.       RTEZ_ReqTitle, "Status",
  297.       TAG_END );
  298.    return handle;
  299. }
  300.  
  301. // remove a Post-It=AE note from the screen
  302. void unpost_it(handle)
  303. struct rtHandlerInfo *handle;
  304. {
  305.    rtReqHandler(handle,NULL,RTRH_EndRequest,TRUE,TAG_END);
  306. }
  307.  
  308.  
  309. int left_buttonP()
  310. {  // returns TRUE if left mouse button is down
  311.    char *pra = (char *)0xBFE001;  // CIA-A port register A
  312.  
  313.    if ( (*pra & 0x40)==0 )
  314.       return TRUE;
  315.    else
  316.       return FALSE;
  317. }
  318.  
  319.  
  320.  
  321. void set_title_palette()
  322. {
  323.    struct ViewPort *ScreenViewPort;
  324.  
  325.    UWORD MyPalette[16] = {  // the #define colors relate to these
  326.       0x0000, 0x0000,
  327.       0x0000, 0x0000,
  328.       0x0000, 0x0000,
  329.       0x0000, 0x0000,
  330.       0x0000, 0x000F,
  331.       0x0142, 0x0F00,
  332.       0x099B, 0x0ED9,
  333.       0x0FF0, 0x0FEE
  334.    };
  335.  
  336.    ScreenViewPort = &title_screen->ViewPort;
  337.    LoadRGB4(ScreenViewPort,MyPalette,16L);
  338. }
  339.  
  340.  
  341.  
  342. void open_title_screen()
  343. {
  344.    //short my_pens[] = { ~0 };
  345.  
  346.    // open the screen
  347.    struct NewScreen new_title_screen = {
  348.       0,0,       // x/y position
  349.       320,200,   // x/y size
  350.       4,         // depth
  351.       BLACK,         // detail pen
  352.       WHITE,         // block pen
  353.       NULL,    // ViewModes
  354.       CUSTOMSCREEN|CUSTOMBITMAP|SCREENQUIET,  // Type
  355.       NULL,  // font
  356.       NULL,  // default title; change it in a second
  357.       NULL,  // gadgets
  358.       NULL   // custom bitmap
  359.    };
  360.  
  361.  
  362.       new_title_screen.CustomBitMap = &title_bmp;
  363.  
  364.       title_screen=(struct Screen *)OpenScreen(&new_title_screen);
  365.       if (title_screen==NULL) {
  366.                 clean_exit(1,"ERROR: Unable to open title screen.\n");
  367.       FI
  368.       set_title_palette();
  369. }
  370.  
  371. void load_title_graphics()
  372. {
  373.    InitBitMap(&title_bmp,4,320,200);  // initialize my custom bitmap
  374.  
  375.    title_bmp.Planes[0] = (PLANEPTR)title_picture;
  376.    title_bmp.Planes[1] = (PLANEPTR)title_picture+8000;
  377.    title_bmp.Planes[2] = (PLANEPTR)title_picture+16000;
  378.    title_bmp.Planes[3] = (PLANEPTR)title_picture+24000;
  379. }
  380.  
  381. void title_show()
  382. {
  383.         int music;
  384.  
  385.    //load_title_graphics();
  386.    //open_title_screen();
  387.         music = GetPlayer(0);
  388.         if (music != 0)
  389.                 clean_exit(1,"ERROR: Unable to open music player!");
  390.  
  391.         PlayModule(LoadModule("PROGDIR:Data/Music/princessofdawn.med"));
  392.    //SetTempo(33);
  393.         system("vts title2.ham");
  394.         //wait_for_click();
  395.    //CloseScreen(title_screen);
  396.         UnLoadModule(0);
  397.         FreePlayer();
  398. }
  399.  
  400.  
  401.  
  402.  
  403. void open_map_screen()
  404. {  // this will be the custom screen for the map display
  405.    short my_pens[] = { ~0 };
  406.  
  407.    // open the screen
  408.    map_screen = OpenScreenTags(NULL,
  409.       SA_Type,       CUSTOMSCREEN,
  410.       SA_DetailPen,  BLACK,
  411.       SA_BlockPen,   WHITE,
  412.       SA_Title,      "Invasion Force",
  413.       SA_Font,       &topaz11,
  414.       SA_DisplayID,  prefs.DisplayID,
  415.       SA_Overscan,   prefs.OverscanType,
  416.       SA_Width,      prefs.DisplayWidth,
  417.       SA_Height,     prefs.DisplayHeight,
  418.       SA_AutoScroll, TRUE,
  419.       SA_Depth,      4,
  420.       SA_Pens,       my_pens,
  421.       TAG_END );
  422.    if (map_screen==NULL)
  423.       map_screen = OpenScreenTags(NULL,
  424.          SA_Type,       CUSTOMSCREEN,
  425.          SA_DetailPen,  BLACK,
  426.          SA_BlockPen,   WHITE,
  427.          SA_Title,      "Invasion Force",
  428.          SA_Font,       &topaz11,
  429.          SA_DisplayID,  HIRESLACE_KEY,
  430.          SA_Width,      640,
  431.          SA_Height,     400,
  432.          SA_AutoScroll, TRUE,
  433.          SA_Depth,      4,
  434.          SA_Pens,       my_pens,
  435.          TAG_END );
  436.    if (map_screen==NULL)
  437.       clean_exit(1,"ERROR: Unable to open map screen!");
  438.  
  439.    prefs.DisplayWidth = map_screen->Width;
  440.    prefs.DisplayHeight = map_screen->Height;
  441.    save_prefs();
  442.  
  443.    vi = GetVisualInfo(map_screen,TAG_END);
  444. }
  445.  
  446.  
  447. void open_map_window()
  448. {  // open the map window, which the game is built upon
  449.    int wide, high;
  450.  
  451.    map_window = OpenWindowTags(NULL,
  452.       WA_CustomScreen,map_screen,
  453.       WA_Title,"Top Level",
  454.       WA_Top,           map_screen->BarHeight+1,
  455.       WA_Height,        map_screen->Height-map_screen->BarHeight-1,
  456.       WA_IDCMP,IDCMP_MENUPICK|IDCMP_VANILLAKEY,
  457.       WA_Flags,WFLG_BACKDROP|WFLG_ACTIVATE|NOCAREREFRESH,
  458.       TAG_END );
  459.    if (map_window==NULL)
  460.       clean_exit(1,"ERROR: Unable to open map window!");
  461.  
  462.    // set this window as my default for graphics operations
  463.    rast_port = map_window->RPort;
  464.  
  465.    // calculate hex display size
  466.    // This will adapt my graphics functions to fit whatever display size has been opened.
  467.  
  468.    // first, the width in pixels (-10 for movement bar meter)
  469.    wide = map_window->Width-map_window->BorderLeft-map_window->BorderRight;
  470.  
  471.    // subtract extra room needed for various things on the borders, then divide by hex
  472.    // width (30) and add one extra hex if there is any remainder
  473.    // figure 13 pixels for the left border area; 29 for the right border and scroller
  474.    wide -= (12+18);
  475.    disp_wd = (wide/30)+(((wide%30)!=0)?1:0);
  476.  
  477.    // next, the height in pixels, calculated much as we did for width
  478.    high = map_window->Height-map_window->BorderTop-map_window->BorderBottom;
  479.    high -= (1+13);
  480.    disp_ht = (high/24)+(((high%24)!=0)?1:0);
  481. }
  482.  
  483.  
  484. void set_default_palette(scrn)
  485. struct Screen *scrn;
  486. {  // Set the basic 16-color screen palette for the game.
  487.    struct ViewPort *screen_view_port;
  488.  
  489.    UWORD default_palette[16] = {  // the #define colors relate to these
  490.       0x0AAA, 0x0000,
  491.       0x0EEE, 0x066F,
  492.       0x0CA8, 0x0444,
  493.       0x090C, 0x0F80,
  494.       0x0C00, 0x001D,
  495.       0x033F, 0x0852,
  496.       0x0777, 0x0262,
  497.       0x06C6, 0x0494
  498.    };
  499.  
  500.    screen_view_port = &scrn->ViewPort;
  501.    LoadRGB4(screen_view_port,default_palette,16L);
  502. }
  503.  
  504.  
  505. // check for required font(s), currently only Topaz 11
  506. // if it doesn't find what it needs, it will deliver a FATALITY!
  507. void check_fonts()
  508. {
  509.    struct TextFont *font;
  510.    UWORD font_size;
  511.    char errmsg[] = "ERROR: Unable to open Topaz 11 font!";
  512.  
  513.    // Set the DESIGNED flag bit so it won't try to rescale for me.
  514.    topaz11.ta_Flags |= FPF_DESIGNED;
  515.    topaz11.ta_YSize = 11;
  516.  
  517.    font = OpenDiskFont(&topaz11);
  518.    if (font) {
  519.       font_size = font->tf_YSize;
  520.       CloseFont(font);
  521.       if (font_size!=11)
  522.          clean_exit(1,errmsg);
  523.    } else
  524.       clean_exit(1,errmsg);
  525. }
  526.  
  527.  
  528. void seed_random()
  529. {  // kick off the random number generator
  530.    time_t t;
  531.  
  532.    time(&t);
  533.    RangeSeed = t;
  534.  
  535.    /*
  536.       Note on random numbers: the function RangeRand() is not adequately
  537.       documented in the RKM.  RangeRand(a) returns a number from 0 though
  538.       a-1.  Thus RangeRand(10L) will give a number from 0 through 9.
  539.    */
  540. }
  541.  
  542.  
  543. void create_console()
  544. {  // set up my own console output for debugging and error reports
  545.    if (Output()==NULL)
  546.       my_console=Open("CON:20/20/500/100/Invasion Force",MODE_OLDFILE);
  547.    print("The Game of Invasion Force\n");
  548.    print("Version 0.17 - by Tony Belding\n");
  549. }
  550.  
  551.  
  552. void build_main_gadget_list()
  553. {  // prepare the scroller gadgets for use
  554.    struct NewGadget new_vert_scroller = {
  555.       620,14,     // leftedge, topedge
  556.       16,358,     // width, height
  557.       NULL,       // text label
  558.       NULL,       // font
  559.       1,          // gadget ID
  560.       NULL,       // flags
  561.       NULL,       // set to retval of GetVisualInfo()
  562.       NULL        // UserData
  563.    };
  564.    struct NewGadget new_horz_scroller = {
  565.       4,372,      // leftedge, topedge
  566.       616,12,     // width, height
  567.       NULL,       // text label
  568.       NULL,       // font
  569.       2,          // gadget ID
  570.       NULL,       // flags
  571.       NULL,       // set to retval of GetVisualInfo()
  572.       NULL        // UserData
  573.    };
  574.  
  575.    // window adaptive values for the scroller gadgets
  576.    new_vert_scroller.ng_LeftEdge = map_window->Width-map_window->BorderRight-16;
  577.    new_vert_scroller.ng_Height = map_window->Height-map_window->BorderTop-map_window->BorderBottom-12;
  578.    new_horz_scroller.ng_TopEdge = map_window->Height-map_window->BorderBottom-12;
  579.    new_horz_scroller.ng_Width = map_window->Width-map_window->BorderLeft-map_window->BorderRight-16;
  580.  
  581.    new_vert_scroller.ng_VisualInfo = new_horz_scroller.ng_VisualInfo = vi;
  582.    if (!CreateContext(&context))
  583.       clean_exit(1,"ERROR: Unable to create context gadget!");
  584.    vert_scroller = CreateGadget(SCROLLER_KIND,context,&new_vert_scroller,
  585.       GTSC_Arrows,13L,
  586.       PGA_Freedom,LORIENT_VERT,
  587.       GA_RelVerify,TRUE,
  588.       TAG_END );
  589.    horz_scroller = CreateGadget(SCROLLER_KIND,vert_scroller,&new_horz_scroller,
  590.       GTSC_Arrows,16L,
  591.       PGA_Freedom,LORIENT_HORIZ,
  592.       GA_RelVerify,TRUE,
  593.       TAG_END );
  594.    if (vert_scroller==NULL || horz_scroller==NULL)
  595.       clean_exit(1,"ERROR: Unable to create scroller gadgets!");
  596.  
  597.    //efmV added code to allocate memory for  ->SpecialInfo
  598.    // NOTE: The OS is supposed to allocate and intialize this, but some
  599.    //       versions fail to do so.  The adaptive code will check for this
  600.    //       and correct it.
  601.    if (horz_scroller->SpecialInfo==NULL)
  602.       hs_si = horz_scroller->SpecialInfo = AllocVec(sizeof(struct PropInfo),0);
  603.    if (horz_scroller->SpecialInfo==NULL)
  604.       clean_exit(1,"allocation error");
  605.    if (vert_scroller->SpecialInfo==NULL);
  606.       vs_si = vert_scroller->SpecialInfo = AllocVec(sizeof(struct PropInfo),0);
  607.    if (horz_scroller->SpecialInfo==NULL)
  608.       clean_exit(1,"allocation error");
  609.    //end of what I added
  610.  
  611.    ((struct PropInfo *)(horz_scroller->SpecialInfo))->Flags = FREEHORIZ|PROPNEWLOOK;
  612.    ((struct PropInfo *)(vert_scroller->SpecialInfo))->Flags = FREEVERT|PROPNEWLOOK;
  613.  
  614.    // attach gadgets to window
  615.    AddGList(map_window,context,0,-1,NULL);
  616.    RefreshGList(context,map_window,NULL,-1);
  617.    GT_RefreshWindow(map_window,NULL);
  618. }
  619.  
  620.  
  621. void build_main_menu()
  622. {  // prepare the main opening menu for use
  623.    struct NewMenu new_menu_strip[]  = {
  624.       { NM_TITLE, "Project", NULL, 0, NULL, NULL },
  625.       { NM_ITEM, "About Invasion Force", "A", ITEMTEXT, NULL, NULL },
  626.       { NM_ITEM, NM_BARLABEL, NULL, ITEMTEXT, NULL, NULL },
  627.       { NM_ITEM, "New Game", "N", ITEMTEXT, NULL, NULL },
  628.       { NM_ITEM, "Load Game", "L", ITEMTEXT, NULL, NULL },
  629.       { NM_ITEM, "Map Editor", "M", ITEMTEXT, NULL, NULL },
  630.       { NM_ITEM, "Quit Program", "Q", ITEMTEXT, NULL, NULL },
  631.       { NM_TITLE, "Prefs", NULL, 0, NULL, NULL },
  632.       { NM_ITEM, "Define Sounds", "D", ITEMTEXT, NULL, NULL },
  633.       { NM_ITEM, "Screen Mode", "G", ITEMTEXT, NULL, NULL },
  634.       { NM_END, NULL, NULL, NULL, NULL, NULL }
  635.    };
  636.  
  637.    if (!(main_menu_strip = CreateMenus(new_menu_strip,GTMN_FrontPen,BLACK,TAG_END)))
  638.       clean_exit(1,"ERROR: Unable to create main menu strip!");
  639.  
  640.    if (!(LayoutMenus(main_menu_strip,vi,TAG_END)))
  641.       clean_exit(1,"ERROR: Unable to layout menus!");
  642. }
  643.  
  644.  
  645. void about_empire()
  646. {  // copyright, author, version info and such
  647.    (void)rtEZRequestTags("Invasion Force version 0.17\nby Tony Belding\n\nThis software is free.\nYou may make as many copies as you like.",
  648.       "Okay",NULL,NULL,
  649.       RTEZ_Flags,EZREQF_CENTERTEXT,
  650.       RT_DEFAULT,TAG_END);
  651. }
  652.  
  653.  
  654. void quit_program()
  655. {  // quit the program with the user's confirmation
  656.    if (rtEZRequestTags("Really quit the program?","Exit|Cancel",NULL,NULL,
  657.       RTEZ_ReqTitle,"Exit Invasion Force",
  658.       RT_DEFAULT,TAG_END ) )
  659.       clean_exit(0,NULL);
  660. }
  661.  
  662.  
  663. void load_prefs()
  664. {
  665.    char *filename="progdir:data/iforce.prefs";
  666.    BPTR file;
  667.    short revcheck;
  668.  
  669.    // set default prefs; they will be overwritten if the file is found
  670.    prefs.DisplayID = HIRESLACE_KEY;
  671.    prefs.DisplayWidth = 640;
  672.    prefs.DisplayHeight = 400;
  673.    prefs.OverscanType = OSCAN_TEXT;
  674.  
  675.    if (FLength(filename)<0L) {   // first filename not found
  676.       filename = "progdir:iforce.prefs";  // use second name
  677.       if (FLength(filename)<0L)   // second filename not found
  678.          return;
  679.    FI
  680.  
  681.    file = Open(filename,MODE_OLDFILE);
  682.    if (file==NULL)
  683.       return;
  684.    (void)Read(file,foo,8L);
  685.    if (strncmp(foo,"EMP2PREF",8)) {
  686.       Close(file);
  687.       return;
  688.    FI
  689.    (void)Read(file,&revcheck,2L);
  690.    if (revcheck<7 || revcheck>revision) {
  691.       Close(file);
  692.       return;
  693.    FI
  694.    Read(file,&prefs,sizeof(prefs));
  695.    if (revcheck>=16) {
  696.       int k=0;
  697.  
  698.       for (;k<MAX_SOUNDS;k++) {
  699.          Read(file,foo,216L);
  700.          // we never accept a filename unless we can prove the file exists
  701.          if (FLength(foo)>0)
  702.             strncpy(current_sound[k],foo,215L);
  703.       }
  704.    }
  705.    Close(file);
  706. }
  707.  
  708.  
  709. void save_prefs()
  710. {
  711.    BPTR file=Open("progdir:data/iforce.prefs",MODE_NEWFILE);
  712.  
  713.    if (file==NULL)
  714.       file = Open("progdir:iforce.prefs",MODE_NEWFILE);
  715.    if (file) {
  716.       // the basic header information
  717.       Write(file,"EMP2PREF",8L);    // magic identifies my prefs files
  718.       Write(file,&revision,2L);     // identify revision of Invasion Force
  719.       Write(file,&prefs,sizeof(prefs));
  720.       {
  721.          int k=0;
  722.          for (;k<MAX_SOUNDS;k++)
  723.             Write(file,current_sound[k],216L);
  724.       }
  725.       Close(file);
  726.    FI
  727. }
  728.  
  729.  
  730. void change_screenmode()
  731. {
  732.    struct rtScreenModeRequester *smr=rtAllocRequest(RT_SCREENMODEREQ,NULL);
  733.    int new;
  734.  
  735.    if (smr==NULL)    // can't open the requester
  736.       return;
  737.    new = rtScreenModeRequest(smr,"Select the screen mode...",
  738.       RT_Window,        map_window,
  739.       RT_ReqPos,        REQPOS_CENTERWIN,
  740.       RT_LockWindow,    TRUE,
  741.       RTSC_Flags,       SCREQF_OVERSCANGAD|SCREQF_GUIMODES|SCREQF_SIZEGADS,
  742.       RTSC_MinWidth,    640,
  743.       RTSC_MinHeight,   400,
  744.       TAG_END);
  745.    rtFreeRequest(smr);
  746.  
  747.    if (new) {
  748.       // extract data from smr
  749.       prefs.DisplayID      = smr->DisplayID;
  750.       prefs.DisplayWidth   = smr->DisplayWidth;
  751.       prefs.DisplayHeight  = smr->DisplayHeight;
  752.       prefs.OverscanType   = smr->OverscanType;
  753.       save_prefs();
  754. //      clean_exit(CE_RESTART,NULL);
  755. //      control_flag = CE_RESTART;
  756.  
  757.       // close down the old map_window environment
  758.       if (map_window) {
  759.          ClearMenuStrip(map_window);
  760.          CloseWindow(map_window);
  761.          map_window = NULL;
  762.       FI
  763.       if (map_region) {
  764.          DisposeRegion(map_region);
  765.          map_region = NULL;
  766.       FI
  767.       if (bar_region) {
  768.          DisposeRegion(bar_region);
  769.          bar_region = NULL;
  770.       FI
  771.       if (hs_si) {
  772.          FreeVec(hs_si);
  773.          hs_si = NULL;
  774.       FI
  775.       if (vs_si) {
  776.          FreeVec(vs_si);
  777.          vs_si = NULL;
  778.       FI
  779.       FreeGadgets(context);
  780.       context = NULL;
  781.       if (main_menu_strip) {
  782.          FreeMenus(main_menu_strip);
  783.          main_menu_strip = NULL;
  784.       FI
  785.       if (vi) {
  786.          FreeVisualInfo(vi);
  787.          vi = NULL;
  788.       FI
  789.       if (map_screen) {
  790.          CloseScreen(map_screen);
  791.          map_screen = NULL;
  792.       FI
  793.  
  794.       // open the new one and re-attach everything
  795.       open_map_screen();
  796.       set_default_palette(map_screen);
  797.       open_map_window();
  798.       build_main_gadget_list();
  799.       init_map_grafx();
  800.       clear_movebar();
  801.       build_main_menu();   // prepare the main drop-down menus for use
  802.    FI
  803. }
  804.  
  805.  
  806. void change_sounds()
  807. {
  808.    struct Window *snd_window = NULL;
  809.    struct Gadget *context, *ok_gad, *defaults_gad;
  810.  
  811.    struct Gadget *snd_button[5], *snd_field[5], *play_button[5];
  812.    char snd_label[5][10]={"Alert:","Battle:","Defeat:","No-Go:","Victory:"};
  813.  
  814.    struct NewGadget button = {
  815.       159,219, // leftedge, topedge
  816.       105,16,   // width, height
  817.       "Continue",    // text label
  818.       &topaz11bold,  // font
  819.       1,             // gadget ID
  820.       PLACETEXT_IN,  // flags
  821.       NULL,NULL   // visual info, user data
  822.    };
  823.    struct NewGadget stringfield = {
  824.       77,50,
  825.       300,16,
  826.       "_Name:",
  827.       &topaz11,
  828.       2,
  829.       PLACETEXT_LEFT,
  830.       NULL,NULL
  831.    };
  832.  
  833.    int wd=518, ht=170;
  834.    int left_edge, top_edge;
  835.  
  836.    left_edge = (map_screen->Width-wd)/2;
  837.    top_edge = (map_screen->Height-ht)/2;
  838.  
  839.    // make sure user doesn't play with the map window now
  840.    SetPointer(map_window,BUSY_POINTER);
  841.    ModifyIDCMP(map_window,NULL);
  842.  
  843.    if (!CreateContext(&context))
  844.       clean_exit(1,"Unable to create context gadget!");
  845.  
  846.    {
  847.       int i;
  848.       struct Gadget *previous = context;
  849.  
  850.       // the [SND] requester buttons
  851.       button.ng_VisualInfo = vi;
  852.       button.ng_LeftEdge = 12;
  853.       button.ng_TopEdge = 18;
  854.       button.ng_Width = 80;
  855.       button.ng_Height = 15;
  856.       button.ng_TextAttr = &topaz11;
  857.  
  858.       for (i=0; i<MAX_SOUNDS; i++) {
  859.          button.ng_GadgetText = snd_label[i];
  860.          previous = snd_button[i] = CreateGadget(BUTTON_KIND,previous,&button,TAG_END);
  861.          button.ng_TopEdge += 20;
  862.       }
  863.  
  864.       // the [play] buttons
  865.       button.ng_LeftEdge = 458;
  866.       button.ng_TopEdge = 18;
  867.       button.ng_Width = 48;
  868.       button.ng_Height = 15;
  869.       button.ng_TextAttr = &topaz11;
  870.       button.ng_GadgetText = "play";
  871.  
  872.       for (i=0; i<MAX_SOUNDS; i++) {
  873.          previous = play_button[i] = CreateGadget(BUTTON_KIND,previous,&button,TAG_END);
  874.          button.ng_TopEdge += 20;
  875.       }
  876.  
  877.       // create string gadget for SND filenames
  878.       stringfield.ng_VisualInfo = vi;
  879.       stringfield.ng_LeftEdge = 100;
  880.       stringfield.ng_TopEdge = 18;
  881.       stringfield.ng_Width = 350;
  882.       stringfield.ng_Height = 16;
  883.       stringfield.ng_GadgetText = NULL;
  884.  
  885.       for (i=0; i<MAX_SOUNDS; i++) {
  886.          previous = snd_field[i] = CreateGadget(STRING_KIND,previous,&stringfield,
  887.             GTST_String,   current_sound[i],
  888.             GTST_MaxChars, 115L,
  889.             STRINGA_Justification, GACT_STRINGCENTER,
  890.             TAG_END);
  891.          stringfield.ng_TopEdge += 20;
  892.       }
  893.    }
  894.  
  895.    // create the [Restore Defaults] and [I'm Done] buttons
  896.    button.ng_VisualInfo = vi;
  897.    button.ng_Width = 148;
  898.    button.ng_LeftEdge = (wd-button.ng_Width)/2;
  899.    button.ng_TopEdge = stringfield.ng_TopEdge+5;
  900.    button.ng_GadgetText = "Restore Defaults";
  901.    button.ng_TextAttr = &topaz11;
  902.    defaults_gad = CreateGadget(BUTTON_KIND,snd_field[MAX_SOUNDS-1],&button,TAG_END);
  903.  
  904.    button.ng_Width = 100;
  905.    button.ng_LeftEdge = (wd-button.ng_Width)/2;
  906.    button.ng_TopEdge += 20;
  907.    button.ng_GadgetText = "I'm Done";
  908.    button.ng_TextAttr = &topaz11bold;
  909.    ok_gad = CreateGadget(BUTTON_KIND,defaults_gad,&button,TAG_END);
  910.  
  911.    left_edge = (map_screen->Width-wd)/2;
  912.    top_edge = (map_screen->Height-ht)/2;
  913.  
  914.    snd_window = OpenWindowTags(NULL,
  915.       WA_Gadgets,       context,
  916.       WA_Title,         "Game Sound Preferences",
  917.       WA_CustomScreen,  map_screen,
  918.       WA_Top,           top_edge,
  919.       WA_Left,          left_edge,
  920.       WA_Height,        ht,
  921.       WA_Width,         wd,
  922.       WA_IDCMP,         IDCMP_GADGETUP|IDCMP_VANILLAKEY,
  923.       WA_Flags,         WFLG_SMART_REFRESH|WFLG_DRAGBAR|WFLG_ACTIVATE,
  924.       TAG_END );
  925.    if (snd_window==NULL)
  926.       clean_exit(1,"ERROR: Unable to open player sound prefs window!");
  927.    GT_RefreshWindow(snd_window,NULL);
  928.    rast_port = snd_window->RPort;
  929.  
  930.    {  // handle the user actions here
  931.       struct IntuiMessage *message; // the message the IDCMP sends us
  932.  
  933.       // useful for interpreting IDCMP messages
  934.       UWORD code;
  935.       ULONG class;
  936.       APTR object;
  937.       UWORD qualifier;
  938.  
  939.       FOREVER {
  940.          WaitPort(snd_window->UserPort);
  941.          while (message = GT_GetIMsg(snd_window->UserPort)) {
  942.             code = message->Code;  // MENUNUM
  943.             object = message->IAddress;  // Gadget
  944.             class = message->Class;
  945.             qualifier = message->Qualifier;
  946.             GT_ReplyIMsg(message);
  947.             if (class==IDCMP_VANILLAKEY) {
  948.                if (code==13) {
  949.                   // show the button depressed
  950.                   show_depress(ok_gad,snd_window->RPort);
  951.                   Delay(10L);
  952.                   goto exit_snd_window;
  953.                FI
  954.             FI
  955.             if (class==IDCMP_GADGETUP) {
  956.                int sfx;
  957.  
  958.                if (object==ok_gad)
  959.                   goto exit_snd_window;
  960.  
  961.                if (object==defaults_gad) {
  962.                   int i;
  963.  
  964.                   for (i=0; i<MAX_SOUNDS; i++) {
  965.                      strcpy(current_sound[i],default_sound[i]);
  966.                      GT_SetGadgetAttrs(snd_field[i],snd_window,NULL,
  967.                         GTST_String,   current_sound[i],
  968.                         TAG_DONE);
  969.  
  970.                      loadSound(current_sound[i],&sdata[i]);
  971.                   }
  972.                   continue;
  973.                }
  974.  
  975.                /* See if a button has been hit. */
  976.                sfx=0;
  977.                while (sfx<MAX_SOUNDS) {
  978.                   if (object==snd_button[sfx])
  979.                      break;
  980.                   sfx++;
  981.                }
  982.                if (sfx<MAX_SOUNDS) {   /* We have a match! */
  983.                   struct rtFileRequester *req = NULL;
  984.                   char path[216], name[216];
  985.                   BOOL chosen;
  986.  
  987.                   req = rtAllocRequestA(RT_FILEREQ,NULL);
  988.                   if (req==NULL) {
  989.                      alert(snd_window,NULL,"Failed to allocate ReqTools file requester.","Drat!");
  990.                      continue;
  991.                   FI
  992.  
  993.                   /* split the pan into path and name */
  994.                   {
  995.                      char *pan=current_sound[sfx];
  996.                      int i, j;
  997.  
  998.                      path[0]='\0';
  999.                      strncpy(name,pan,216L);
  1000.                      i = j = strlen(pan);
  1001.                      while (i>0) {
  1002.                         i--;
  1003.                         if (pan[i]==':' || pan[i]=='/') {
  1004.                            strncpy(path,pan,i);
  1005.                            path[i] = '\0';
  1006.                            if (i<j) {
  1007.                               strncpy(name,pan+i+1,j-i);
  1008.                               name[j-i] = '\0';
  1009.                            }
  1010.                            break;
  1011.                         }
  1012.                      }
  1013.                   }
  1014.  
  1015.                   rtChangeReqAttr(req,
  1016.                      RTFI_Dir,      path,
  1017.                      RTFI_MatchPat, "#?",
  1018.                      TAG_END);
  1019.  
  1020.                   chosen = (BOOL)rtFileRequest(req,name,"Select Sound",
  1021.                      RTFI_Flags, FREQF_PATGAD,
  1022.                      RT_DEFAULT, TAG_END);
  1023.  
  1024.  
  1025.                   if (chosen) {
  1026.                      int dirlen = strlen(req->Dir);
  1027.                      long l;
  1028.  
  1029.                      strcpy(foo,req->Dir);
  1030.                      if (dirlen)
  1031.                         if (foo[dirlen-1]!='/' && foo[dirlen-1]!=':')
  1032.                            strcat(foo,"/");
  1033.                      strcat(foo,name);
  1034.  
  1035.                      /* See if the file really exists. */
  1036.                      l = FLength(foo);
  1037.                      if (l<=0L)
  1038.                         alert(snd_window,NULL,
  1039.                            "There seems to be a problem with that file!",
  1040.                            "Drat!");
  1041.                      else {
  1042.                         // copy pan into the string gadget
  1043.                         strncpy(current_sound[sfx],foo,216L);
  1044.                         GT_SetGadgetAttrs(snd_field[sfx],snd_window,NULL,
  1045.                            GTST_String,   current_sound[sfx],
  1046.                            TAG_DONE);
  1047.  
  1048.                         loadSound(current_sound[sfx],&sdata[sfx]);
  1049.                         playSound(sfx,64);
  1050.                      }
  1051.                   FI
  1052.                   rtFreeRequest(req);
  1053.                   continue;
  1054.                FI    // end of SOUND BUTTONS handling
  1055.  
  1056.                /* See if a text field has been edited. */
  1057.                sfx=0;
  1058.                while (sfx<MAX_SOUNDS) {
  1059.                   if (object==snd_field[sfx])
  1060.                      break;
  1061.                   sfx++;
  1062.                }
  1063.                if (sfx<MAX_SOUNDS) {   /* We have a match! */
  1064.                   struct StringInfo *stringthing=snd_field[sfx]->SpecialInfo;
  1065.                   long l;
  1066.  
  1067.                   strcpy(foo,stringthing->Buffer);
  1068.                   l = FLength(foo);
  1069.                   if (l<=0L) {
  1070.                      alert(snd_window,NULL,
  1071.                         "There seems to be a problem with that file!",
  1072.                         "Drat!");
  1073.                      // change it back to original
  1074.                      GT_SetGadgetAttrs(snd_field[sfx],snd_window,NULL,
  1075.                         GTST_String,   current_sound[sfx],
  1076.                         TAG_DONE);
  1077.                   } else {
  1078.                      strcpy(current_sound[sfx],foo);
  1079.                      loadSound(current_sound[sfx],&sdata[sfx]);
  1080.                      playSound(sfx,64);
  1081.                   }
  1082.                   continue;
  1083.                FI    // end of TEXT FIELD handling
  1084.  
  1085.                /* See if a button has been hit. */
  1086.                sfx=0;
  1087.                while (sfx<MAX_SOUNDS) {
  1088.                   if (object==play_button[sfx])
  1089.                      break;
  1090.                   sfx++;
  1091.                }
  1092.                if (sfx<MAX_SOUNDS) {   /* We have a match! */
  1093.                   playSound(sfx,64);
  1094.                   continue;
  1095.                FI    // end of PLAY BUTTONS handling
  1096.  
  1097.             FI
  1098.          OD
  1099.       OD
  1100.    OD
  1101.  
  1102.  exit_snd_window:
  1103.  
  1104.    save_prefs();
  1105.  
  1106.    // now close up everything with the prefs_window
  1107.    CloseWindow(snd_window);
  1108.    snd_window = NULL;
  1109.    FreeGadgets(context);
  1110.  
  1111.    // reset everything for map window
  1112.    rast_port = map_window->RPort;
  1113.    ClearPointer(map_window);
  1114.    ModifyIDCMP(map_window,IDCMP_MENUPICK);
  1115. }
  1116.  
  1117.  
  1118. void main_menu()
  1119. {  // handle the main opening menu
  1120.    struct IntuiMessage *message; // the message the IDCMP sends us
  1121.  
  1122.    // useful for interpreting IDCMP messages
  1123.    UWORD code;
  1124.    ULONG class;
  1125.  
  1126.    // attach the menu to my window
  1127.    SetMenuStrip(map_window,main_menu_strip);
  1128.  
  1129.    // enable menus
  1130.    OnMenu(map_window,FULLMENUNUM(0,NOITEM,0));
  1131.    OnMenu(map_window,FULLMENUNUM(1,NOITEM,0));
  1132.  
  1133. //   OffMenu(map_window,FULLMENUNUM(1,0,0));
  1134.  
  1135.    while (TRUE) {
  1136.       WaitPort(map_window->UserPort);
  1137.       while (message = GT_GetIMsg(map_window->UserPort)) {
  1138.          code = message->Code;  // MENUNUM
  1139.          class = message->Class;
  1140.          GT_ReplyIMsg(message);
  1141.  
  1142.          if ( class == MENUPICK ) { // MenuItems
  1143.             OffMenu(map_window,FULLMENUNUM(0,-1,0));
  1144.             OffMenu(map_window,FULLMENUNUM(1,-1,0));
  1145.             switch (MENUNUM(code)) {
  1146.                case 0:  // the Project menu
  1147.                   switch (ITEMNUM(code)) {
  1148.                      case 0:  // About Invasion Force
  1149.                         about_empire();
  1150.                         break;
  1151.                      case 4:  // Map Editor
  1152.                         map_editor();
  1153.                         break;
  1154.                      case 5:  // Quit
  1155.                         clean_exit(0,NULL);
  1156.                         break;
  1157.                      case 2:  // New Game
  1158.                         if (!edit_options())
  1159.                            break;
  1160.                         play_game();
  1161.                         break;
  1162.                      case 3:  // Load game
  1163.                         if (rt_loadsave_game(FALSE))
  1164.                            restore_game();
  1165.                   }
  1166.                   break;
  1167.                case 1:  // the prefs menu
  1168.                   switch (ITEMNUM(code)) {
  1169.                      case 0: // Define Sounds
  1170.                         change_sounds();
  1171.                         break;
  1172.                      case 1:  // Screen Mode
  1173.                         ClearMenuStrip(map_window);
  1174.                         change_screenmode();
  1175.                         SetMenuStrip(map_window,main_menu_strip);
  1176. //                        if (control_flag==CE_RESTART)
  1177. //                           return;
  1178.                   }
  1179.             }
  1180.             OnMenu(map_window,FULLMENUNUM(0,NOITEM,0));
  1181.             OnMenu(map_window,FULLMENUNUM(1,NOITEM,0));
  1182.          FI
  1183.       OD
  1184.    OD
  1185. }
  1186.  
  1187.  
  1188. void main()
  1189. {
  1190.    // initialize everything
  1191.    control_flag = 0;
  1192.    create_console();  // create my debugging console
  1193.    open_libraries();
  1194.    seed_random();     // seed the random number generator.
  1195.    check_fonts();     // see if I have all the fonts I need
  1196.    title_show();
  1197.  
  1198.    {
  1199.       /*
  1200.          default sound effects: these sounds aren't loaded yet, they may
  1201.          be changed momentarily by the user prefs
  1202.       */
  1203.       int k=0;
  1204.       for (;k<MAX_SOUNDS;k++)
  1205.          strncpy(current_sound[k],default_sound[k],215L);
  1206.    }
  1207.  
  1208.    /* prefs load before opening the screen, so we know what screenmode */
  1209.    load_prefs();
  1210.  
  1211.    open_map_screen();      // put together my display
  1212.    set_default_palette(map_screen);
  1213.    open_map_window();
  1214.    build_main_gadget_list();
  1215.    init_map_grafx();
  1216.    clear_movebar();
  1217.    build_main_menu();   // prepare the main drop-down menus for use
  1218.  
  1219.    initSounds();        // prepare sound effects for use
  1220.  
  1221.    NewList((struct List *)&city_list);  // initialize the city list
  1222.    NewList((struct List *)&unit_list);  // initialize the active units list
  1223.    NewList((struct List *)&GovList);    // initialize the governors list for
  1224.                                         // the AI players
  1225.  
  1226.    // initialize default PAN (Path And Name)
  1227.    strcpy(game_filepath,"ProgDir:Games/");
  1228.    strcpy(game_filename,"Game.IF");
  1229.  
  1230.    // jump into the program itself
  1231.    main_menu();
  1232.    if (control_flag==CE_RESTART)
  1233.       main();
  1234.    clean_exit(0,NULL);
  1235. }
  1236.  
  1237. // end of listing
  1238.  
  1239.  
  1240.  
  1241.